package com.mars.designpatterns.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * @Author mars
 * @Description 单例模式：
 * 1.特点：
 *  》 类构造器私有
 *  》 持有自己类型的属性
 *  》 对外提供获取实例的静态方法
 * 2.栗子a：懒汉单例模式:(线程不安全,使用的时候才会初始化)
 *        》 声明一个静态的单例对象 null,初始化的时候不创建
 *        》 私有构造方法
 *        》 创建一个对外提供获取对象实例的静态方法
 * 3.栗子b：多线程情况下懒汉式单例模式的例子
 * 4.改造：
 *   》 方法一： 在静态方法上添加 synchronized,锁的是class类,在普通方法添加synchronized，锁的是堆内存的对象
 *      public static synchronized LazySingleton getInstance(){}
 *   》 方法二： 使用synchronized锁类代码块
 *       synchronized (LazySingleton.class) {
 *             if(lazySingleton==null) {
 *                 lazySingleton = new LazySingleton();
 *             }
 *             return lazySingleton;
 *         }
 *  5.破坏反射攻击：
 *
 * @Date
 * @return
 */
public class LazySingleton {

    private static LazySingleton lazySingleton = null;

    private static boolean flag = true;

    private LazySingleton() {
        if (flag) {
            flag = false;
        } else {
            throw new RuntimeException("单例构造器禁止反射调用");
        }


        // 以下为防止反射攻击的代码
        //if(lazySingleton!=null){
        //    throw new RuntimeException("单例构造器禁止反射调用");
        //}
    }

    public static synchronized LazySingleton getInstance(){
        if(lazySingleton==null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    public static void main(String[] args) throws Exception {
        Class objectClass =  LazySingleton.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        // 将私有构造器的权限进行修改，设置为true
        constructor.setAccessible(true);
        LazySingleton o1 = LazySingleton.getInstance();

        // 反射攻击,从class获取flag的字段进行修改
        Field flag = o1.getClass().getDeclaredField("flag");//获取该类中的flag字段
        flag.setAccessible(true);//因为flag字段是私有的，所以设置开放权限，可修改
        flag.set(o1,true);// 重新给flag字段赋值

        LazySingleton o2 = (LazySingleton)constructor.newInstance();

        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o1 == o2);

    }
}
